#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
参数优化器 - 反推合理参数区间

功能：
1. 分析特定股票、特定日期的数据
2. 反推触发特定策略所需的参数区间
3. 避免频繁回测，快速找到合理参数

使用示例：
    python parameter_optimizer.py --stock 002594.SZ --date 20250523 --target sell_A
    
默认配置（可直接修改后运行）:
"""

import sys
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import argparse
from pathlib import Path
import yaml

# 导入项目模块
sys.path.append(str(Path(__file__).parent))
from xtquant import xtdata

# ============================================================================
# 📝 默认参数配置（可直接修改这里，然后运行: python parameter_optimizer.py）
# ============================================================================
DEFAULT_CONFIG = {
    'stock': '002594.SZ',              # 股票代码
    'dates': [                         # 📌 分析日期列表（只需填写日期）
        # 示例：
        '20250523',
        '20250829',
        # 说明：工具会自动在10:00分析（开盘后30分钟，数据充足）
    ],
    'target': 'tech_upper_shadow',     # 目标策略类型
    'dividend_base': '20251101',       # 前复权基准日（应与回测结束日期一致！）
}

# 📌 目标策略说明：
#   - tech_upper_shadow : 技术形态上影线（卖出信号）
#   - sell_A           : 条件A - 高位缩量卖出
#   - sell_B1          : 条件B1 - 天量出货
#   - sell_B2          : 条件B2 - 放量滞涨
#   - sell_C           : 条件C - 跌破5日线放量确认
# ============================================================================


class ParameterOptimizer:
    """参数优化器 - 反推参数区间"""
    
    def __init__(self):
        """初始化优化器"""
        # 加载配置
        with open('config/config.yaml', 'r', encoding='utf-8') as f:
            self.config = yaml.safe_load(f)
        
        with open('config/technical_patterns.yaml', 'r', encoding='utf-8') as f:
            self.tech_config = yaml.safe_load(f)
        
        print("\n" + "="*70)
        print("   参数优化器 - 反推合理参数区间")
        print("="*70)
    
    def analyze_date(self, stock, date, target_strategy, specific_time=None, dividend_base=None):
        """
        分析特定日期的数据，反推参数
        
        Args:
            stock: 股票代码
            date: 日期（YYYYMMDD）
            target_strategy: 目标策略
            specific_time: 具体时间（可选）
            dividend_base: 前复权基准日（应与回测结束日一致）
        """
        print(f"\n📊 分析股票: {stock}")
        print(f"📅 分析日期: {date}")
        print(f"🎯 目标策略: {target_strategy}")
        if specific_time:
            print(f"⏰ 具体时间: {specific_time}")
        print("="*70)
        
        # ⭐ 自动计算所需历史数据天数（考虑动态阈值配置）
        from datetime import datetime, timedelta
        try:
            from config_loader import get_config_loader
            config_loader = get_config_loader()
            config = config_loader._config  # 获取原始配置字典
            from volume_validators.dynamic_threshold import DynamicVolumeThresholdCalculator
            required_days = DynamicVolumeThresholdCalculator.get_required_history_days(config)
        except Exception as e:
            print(f"[警告] 无法加载动态阈值配置，使用默认120天: {e}")
            required_days = 120
        
        # 计算数据时间范围（自动扩展以满足动态阈值需求）
        start_date = (datetime.strptime(date, '%Y%m%d') - timedelta(days=required_days)).strftime('%Y%m%d')
        end_date = dividend_base if dividend_base else date
        self.data_range = {
            'start': start_date,
            'end': end_date,
            'analysis_date': date,
            'specific_time': specific_time or '全天'
        }
        
        # 1. 获取数据
        print("\n[步骤1] 获取数据...")
        if dividend_base:
            print(f"  ⚠️  使用前复权基准日: {dividend_base}（确保与回测一致）")
        daily_data, minute_data = self._load_data(stock, date, dividend_base)
        
        if daily_data.empty or minute_data.empty:
            print(f"❌ 数据加载失败 - 日期: {date}")
            print(f"   可能原因: 该日期没有数据或数据获取失败")
            return None
        
        print(f"✅ 日线数据: {len(daily_data)} 条")
        print(f"✅ 分钟数据: {len(minute_data)} 条")
        
        # 2. 计算当前指标
        print("\n[步骤2] 计算实际指标...")
        metrics = self._calculate_metrics(stock, daily_data, minute_data, date, specific_time)
        
        # 2.5. 数据有效性检查
        if metrics.get('intraday_range', 0) == 0:
            print("\n⚠️  警告: 日内振幅为0，无法分析技术形态")
            print("    建议: 选择开盘后至少30分钟的时间点（如10:00:00）")
            print(f"    当前时间: {metrics.get('target_time', 'unknown')}")
            print("    价格数据: 高={:.2f}, 低={:.2f}, 当前={:.2f}".format(
                metrics.get('high', 0), metrics.get('low', 0), metrics.get('current_price', 0)
            ))
        
        # 3. 根据目标策略反推参数
        print("\n[步骤3] 反推参数区间...")
        suggestions = self._reverse_engineer_params(target_strategy, metrics, stock)
        
        # 4. 输出结果
        self._print_suggestions(target_strategy, metrics, suggestions, self.data_range)
        
        return suggestions
    
    def _load_data(self, stock, date, dividend_base=None):
        """
        加载日线和分钟数据
        
        Args:
            stock: 股票代码
            date: 分析日期
            dividend_base: 前复权基准日（应与回测结束日一致，避免价格不一致）
        """
        # 日线数据（加载前60天）
        start_date = (datetime.strptime(date, '%Y%m%d') - timedelta(days=120)).strftime('%Y%m%d')
        
        # ⚠️ 关键：使用dividend_base作为end_time，确保与回测系统前复权基准一致
        end_date = dividend_base if dividend_base else date
        
        daily_data_dict = xtdata.get_local_data(
            field_list=[], stock_list=[stock],
            period='1d', start_time=start_date, end_time=end_date,  # ⭐ 使用dividend_base
            dividend_type='front'
        )
        
        # 从字典中提取DataFrame
        if isinstance(daily_data_dict, dict) and stock in daily_data_dict:
            daily_data = daily_data_dict[stock]
        else:
            daily_data = pd.DataFrame()
        
        # 分钟数据
        minute_start = date + "000000"
        minute_end = date + "235959"
        minute_data_dict = xtdata.get_local_data(
            field_list=[], stock_list=[stock],
            period='1m', start_time=minute_start, end_time=minute_end,
            dividend_type='front'
        )
        
        # 从字典中提取DataFrame
        if isinstance(minute_data_dict, dict) and stock in minute_data_dict:
            minute_data = minute_data_dict[stock]
        else:
            minute_data = pd.DataFrame()
        
        return daily_data, minute_data
    
    def _calculate_metrics(self, stock, daily_data, minute_data, date, specific_time):
        """计算关键指标"""
        metrics = {}
        
        # 获取股票类型
        metrics['stock_type'] = self._get_stock_type(stock)
        
        # ⚠️ 关键：截取到分析日期为止，避免使用未来数据
        target_date = pd.Timestamp(datetime.strptime(date, '%Y%m%d'))
        if not isinstance(daily_data.index, pd.DatetimeIndex):
            daily_data.index = pd.to_datetime(daily_data.index)
        daily_data_filtered = daily_data[daily_data.index <= target_date]
        
        # 日线指标（使用截取后的数据）
        if len(daily_data_filtered) >= 60:
            closes = daily_data_filtered['close'].values
            volumes = daily_data_filtered['volume'].values
            
            # 价格位置（使用最近60天）
            recent_high = float(np.max(closes[-60:]))
            recent_low = float(np.min(closes[-60:]))
            current_price = closes[-1]  # 现在是分析日期当天的收盘价
            metrics['price_position'] = (current_price - recent_low) / (recent_high - recent_low) if recent_high > recent_low else 0.5
            metrics['recent_high'] = recent_high
            metrics['recent_low'] = recent_low
            
            # 均量（使用最近20天）
            metrics['vol_ma20'] = float(np.mean(volumes[-20:])) if len(volumes) >= 20 else 0
            
        # 确保索引是DatetimeIndex
        if not isinstance(minute_data.index, pd.DatetimeIndex):
            minute_data.index = pd.to_datetime(minute_data.index)
        
        # 分钟数据指标（找到特定时间点或开盘）
        if specific_time:
            target_time = pd.Timestamp(datetime.strptime(f"{date} {specific_time}", "%Y%m%d %H:%M:%S"))
            mask = minute_data.index <= target_time
            target_minute = minute_data[mask].iloc[-1] if mask.any() else minute_data.iloc[0]
        else:
            target_minute = minute_data.iloc[0]  # 开盘第一分钟
        
        metrics['target_time'] = target_minute.name
        metrics['open'] = minute_data.iloc[0]['open']
        
        # ⭐ 关键：模拟分时回测的行为
        # 在分时回测中，策略从10:00开始逐分钟判断，使用的是"到当前分钟为止"的累计数据
        # 所以参数优化器也应该使用"到目标时间（10:00）为止"的数据，而不是全天数据
        target_time_index = target_minute.name
        mask_cumulative = minute_data.index <= target_time_index
        cumulative_data = minute_data[mask_cumulative]
        
        # 使用到目标时间为止的累计数据（模拟分时回测的today_data）
        metrics['high'] = cumulative_data['high'].max()    # 到目标时间为止的最高价
        metrics['low'] = cumulative_data['low'].min()      # 到目标时间为止的最低价
        metrics['current_price'] = target_minute['close']  # 目标时间点的价格（对应today_data['last']）
        metrics['volume'] = cumulative_data['volume'].sum()  # 到目标时间为止的累计成交量
        
        # 保存全天数据用于参考（但不用于计算）
        metrics['daily_high'] = minute_data['high'].max()
        metrics['daily_low'] = minute_data['low'].min()
        metrics['daily_close'] = minute_data.iloc[-1]['close']
        
        # ⭐ 计算上影线（使用到目标时间为止的累计数据，与分时回测一致）
        # 分时模式下：上影线 = 最高价 - 当前价格（对应technical_engine中intraday_pullback的计算）
        intraday_range = metrics['high'] - metrics['low']
        metrics['intraday_range'] = intraday_range
        metrics['upper_shadow'] = metrics['high'] - metrics['current_price']  # 直接用当前价格
        metrics['upper_shadow_ratio'] = metrics['upper_shadow'] / intraday_range if intraday_range > 0 else 0
        
        # 同时计算全天数据的上影线（用于对比参考）
        daily_range = metrics['daily_high'] - metrics['daily_low']
        daily_body_top = max(metrics['open'], metrics['daily_close'])
        metrics['daily_upper_shadow'] = metrics['daily_high'] - daily_body_top
        metrics['daily_upper_shadow_ratio'] = metrics['daily_upper_shadow'] / daily_range if daily_range > 0 else 0
        
        # ⭐ 计算量比（模拟分时回测的逻辑，使用实际交易分钟数）
        if len(daily_data_filtered) >= 5:
            vol_ma5 = float(np.mean(daily_data_filtered['volume'].values[-5:]))
            # 计算实际交易分钟数（从9:30到目标时间）
            target_time = pd.Timestamp(target_time_index)
            hour = target_time.hour
            minute = target_time.minute
            
            if hour < 11 or (hour == 11 and minute <= 30):
                # 上午盘：9:30-11:30
                actual_trading_minutes = (hour - 9) * 60 + minute - 30
            elif hour >= 13:
                # 下午盘：13:00-15:00
                actual_trading_minutes = 120 + (hour - 13) * 60 + minute
            else:
                # 中午休市时间（11:30-13:00），使用上午的120分钟
                actual_trading_minutes = 120
            
            actual_trading_minutes = max(1, actual_trading_minutes)
            
            # 量比 = 现成交总手数 ÷ (过去5日平均每分钟成交量 × 当日累计开市时间(分钟))
            daily_trading_minutes = 240  # A股每日交易时间240分钟
            past_5_days_avg_per_minute = vol_ma5 / daily_trading_minutes
            current_trading_minutes = min(actual_trading_minutes, daily_trading_minutes)
            
            # 估算当日预期成交量 = 过去5日平均每分钟成交量 × 当前交易分钟数
            expected_volume = past_5_days_avg_per_minute * current_trading_minutes
            
            metrics['volume_ratio'] = metrics['volume'] / expected_volume if expected_volume > 0 else 1.0
            metrics['vol_ma5'] = vol_ma5
            metrics['actual_trading_minutes'] = actual_trading_minutes
        else:
            metrics['volume_ratio'] = 1.0
        
        return metrics
    
    def _get_stock_type(self, stock):
        """获取股票类型（简化版）"""
        # 这里简化处理，实际应该查询市值
        return 'large'  # 默认大盘股
    
    def _reverse_engineer_params(self, target_strategy, metrics, stock):
        """反推参数"""
        suggestions = {}
        
        if target_strategy == 'sell_A':
            # 高位缩量卖出
            suggestions = self._reverse_sell_A(metrics, stock)
        elif target_strategy == 'sell_B1':
            # 天量出货
            suggestions = self._reverse_sell_B1(metrics, stock)
        elif target_strategy == 'sell_B2':
            # 放量滞涨
            suggestions = self._reverse_sell_B2(metrics, stock)
        elif target_strategy == 'sell_C':
            # 跌破5日线
            suggestions = self._reverse_sell_C(metrics, stock)
        elif target_strategy == 'tech_upper_shadow':
            # 技术形态上影线
            suggestions = self._reverse_tech_upper_shadow(metrics, stock)
        
        return suggestions
    
    def _reverse_sell_A(self, metrics, stock):
        """反推条件A参数"""
        stock_type = metrics['stock_type']
        current_config = self.config['sell_high_shrink'][stock_type]
        
        current_volume_ratio = metrics.get('volume_ratio', 1.0)
        current_position = metrics.get('price_position', 0.5)
        
        # 当前配置的阈值
        threshold_volume_ratio = current_config['volume_ratio']
        threshold_vol_ma20 = current_config['vol_ma20_ratio']
        
        suggestions = {
            'current_values': {
                'volume_ratio': current_volume_ratio,
                'price_position': current_position,
                'volume': metrics.get('volume', 0),
                'vol_ma20': metrics.get('vol_ma20', 0),
            },
            'current_thresholds': {
                'volume_ratio': threshold_volume_ratio,
                'vol_ma20_ratio': threshold_vol_ma20,
            },
            'gaps': {
                'volume_ratio': current_volume_ratio - threshold_volume_ratio,
                'vol_ma20_ratio': (metrics.get('volume', 0) / metrics.get('vol_ma20', 1)) - threshold_vol_ma20 if metrics.get('vol_ma20', 0) > 0 else 0,
            },
            'suggested_params': {}
        }
        
        # 反推建议
        if current_volume_ratio >= threshold_volume_ratio:
            suggestions['suggested_params']['volume_ratio'] = f"✅ 当前 {current_volume_ratio:.2f} 已满足"
        else:
            # 需要降低阈值
            safe_threshold = current_volume_ratio * 0.9  # 留10%余量
            suggestions['suggested_params']['volume_ratio'] = f"建议降至 {safe_threshold:.2f} (当前{threshold_volume_ratio})"
        
        return suggestions
    
    def _reverse_sell_B1(self, metrics, stock):
        """反推条件B1参数（天量出货）"""
        return {'message': '条件B1反推功能待实现'}
    
    def _reverse_sell_B2(self, metrics, stock):
        """反推条件B2参数（放量滞涨）"""
        return {'message': '条件B2反推功能待实现'}
    
    def _reverse_sell_C(self, metrics, stock):
        """反推条件C参数（跌破5日线）"""
        return {'message': '条件C反推功能待实现'}
    
    def _reverse_tech_upper_shadow(self, metrics, stock):
        """
        反推技术形态上影线参数
        
        ⭐ 设计思路：
        用户基于日K线判断某天符合上影线形态，但实际回测是分时的。
        本函数基于"全天数据"（日K线）反推参数，使得在分时回测中形成类似形态时能够触发。
        """
        stock_type = metrics['stock_type']
        cap_mapping = {'large': 'large_cap', 'medium': 'medium_cap', 'small': 'small_cap'}
        config_key = cap_mapping.get(stock_type, 'medium_cap')
        current_config = self.tech_config['upper_shadow']['sell_signals'][config_key]
        
        # ⭐ 关键：使用全天数据（日K线）作为主要参考值
        daily_upper_shadow_ratio = metrics.get('daily_upper_shadow_ratio', 0)
        daily_upper_shadow = metrics.get('daily_upper_shadow', 0)
        daily_range = metrics.get('daily_high', 0) - metrics.get('daily_low', 0)
        daily_close = metrics.get('daily_close', 0)
        
        # 10:00的数据（用于对比说明）
        intraday_upper_shadow_ratio = metrics.get('upper_shadow_ratio', 0)
        intraday_range = metrics.get('intraday_range', 0)
        current_price = metrics.get('current_price', 0)
        
        # 使用全天数据计算量比和位置（更准确反映日K线形态）
        # 但对于分时回测，量比需要特殊处理（因为全天成交量在10:00时还未完全形成）
        volume_ratio = metrics.get('volume_ratio', 1.0)  # 保留10:00的量比作为参考
        price_position = metrics.get('price_position', 0.5)  # 价格位置基于收盘价，使用全天数据
        
        # 当前配置阈值
        threshold_shadow = current_config['shadow_ratio']
        threshold_volume = current_config['volume_min']
        threshold_position = current_config['position_min']
        min_range = self.tech_config['upper_shadow']['min_kline_range']
        
        suggestions = {
            'current_values': {
                # ⭐ 主要使用全天数据（日K线）
                'upper_shadow_ratio': daily_upper_shadow_ratio,  # 使用全天数据
                'volume_ratio': volume_ratio,
                'price_position': price_position,
                'intraday_range': daily_range,  # 使用全天振幅
                'upper_shadow': daily_upper_shadow,  # 使用全天上影线
                # 保留10:00数据用于对比说明
                'intraday_upper_shadow_ratio': intraday_upper_shadow_ratio,
                'intraday_range_10am': intraday_range,
                # 详细数据
                'high': metrics.get('high', 0),  # 10:00时的最高价
                'low': metrics.get('low', 0),    # 10:00时的最低价
                'open': metrics.get('open', 0),
                'current_price': current_price,  # 10:00时的价格
                'daily_high': metrics.get('daily_high', metrics.get('high', 0)),
                'daily_low': metrics.get('daily_low', metrics.get('low', 0)),
                'daily_close': daily_close,
                'daily_upper_shadow': daily_upper_shadow,
                'daily_upper_shadow_ratio': daily_upper_shadow_ratio,
            },
            'current_thresholds': {
                'shadow_ratio': threshold_shadow,
                'volume_min': threshold_volume,
                'position_min': threshold_position,
                'min_kline_range': min_range,
            },
            # ⭐ 基于全天数据计算差异
            'gaps': {
                'shadow_ratio': daily_upper_shadow_ratio - threshold_shadow,
                'volume_min': volume_ratio - threshold_volume,
                'position_min': price_position - threshold_position,
                'min_range': daily_range - min_range,
            },
            'suggested_params': {}
        }
        
        # ⭐ 反推建议（基于全天数据，但要考虑分时回测的特点）
        # 1. 上影线比例 - 使用全天数据，但给出合理的阈值建议
        if daily_upper_shadow_ratio >= threshold_shadow:
            suggestions['suggested_params']['shadow_ratio'] = f"✅ 当前日K线 {daily_upper_shadow_ratio:.2%} 已满足阈值 {threshold_shadow:.2%}"
        else:
            # 建议降至全天数据的95%（留5%余量，确保类似形态能触发）
            safe_threshold = daily_upper_shadow_ratio * 0.95
            suggestions['suggested_params']['shadow_ratio'] = f"建议降至 {safe_threshold:.2%} (当前阈值{threshold_shadow:.2%}, 日K线{daily_upper_shadow_ratio:.2%})"
        
        # 2. 量比 - 注意：分时回测中量比是动态的，这里给出参考建议
        if volume_ratio >= threshold_volume:
            suggestions['suggested_params']['volume_min'] = f"✅ 10:00时量比 {volume_ratio:.2f} 已满足阈值 {threshold_volume:.2f}"
        else:
            safe_threshold = volume_ratio * 0.9
            suggestions['suggested_params']['volume_min'] = f"建议降至 {safe_threshold:.2f} (当前阈值{threshold_volume:.2f}, 10:00量比{volume_ratio:.2f})"
        
        # 3. 价格位置 - 使用全天收盘价计算的位置
        if price_position >= threshold_position:
            suggestions['suggested_params']['position_min'] = f"✅ 价格位置 {price_position:.2%} 已满足阈值 {threshold_position:.2%}"
        else:
            safe_threshold = price_position * 0.95
            suggestions['suggested_params']['position_min'] = f"建议降至 {safe_threshold:.2%} (当前阈值{threshold_position:.2%}, 实际位置{price_position:.2%})"
        
        # 4. K线振幅 - 使用全天振幅
        if daily_range >= min_range:
            suggestions['suggested_params']['min_kline_range'] = f"✅ 全天振幅 {daily_range:.2f}元 已满足阈值 {min_range:.2f}元"
        else:
            safe_threshold = daily_range * 0.9
            suggestions['suggested_params']['min_kline_range'] = f"建议降至 {safe_threshold:.2f}元 (当前阈值{min_range:.2f}元, 全天振幅{daily_range:.2f}元)"
        
        return suggestions
    
    def analyze_multiple_dates(self, stock, dates_list, target_strategy, dividend_base=None):
        """
        批量分析多个日期
        
        Args:
            stock: 股票代码
            dates_list: 日期列表，格式：['20250210 09:31:00', '20250523 09:57:00', ...]
            target_strategy: 目标策略
            dividend_base: 前复权基准日
        
        Returns:
            list: 每个日期的分析结果
        """
        print(f"\n{'='*70}")
        print(f"[批量分析] 股票={stock} | 共{len(dates_list)}个时间点")
        print("="*70)
        
        all_results = []
        
        for i, date_time_str in enumerate(dates_list, 1):
            # 解析日期（统一使用10:00:00，开盘后30分钟数据充足）
            date = date_time_str.strip()[:8]  # 只取前8位作为日期
            time = '10:00:00'  # 固定使用10:00（开盘后30分钟）
            
            print(f"\n{'═'*70}")
            print(f"【第 {i}/{len(dates_list)} 个日期】分析时间点: {date} {time}")
            print("═"*70)
            
            # 单独分析
            result = self.analyze_date(
                stock=stock,
                date=date,
                target_strategy=target_strategy,
                specific_time=time,
                dividend_base=dividend_base
            )
            
            all_results.append({
                'date': date,
                'time': time,
                'result': result
            })
        
        # 汇总统计
        self._print_batch_summary(all_results, target_strategy, stock)
        
        return all_results
    
    def _print_batch_summary(self, results, target_strategy, stock):
        """打印批量分析汇总"""
        print(f"\n{'='*70}")
        print("   📊 批量分析汇总")
        print("   ⭐ 说明: 统计基于日K线数据，参数建议用于分时回测中触发类似形态")
        print("="*70)
        
        # 指标中英文对照
        indicator_names = {
            'shadow_ratio': '上影线比例',
            'volume_min': '量比',
            'position_min': '价格位置',
        }
        
        # 统计满足/不满足的条件
        condition_stats = {
            'shadow_ratio': {'pass': 0, 'fail': 0, 'values': []},
            'volume_min': {'pass': 0, 'fail': 0, 'values': []},
            'position_min': {'pass': 0, 'fail': 0, 'values': []},
        }
        
        # 键名映射：从 gaps 的键名映射到 current_values 的键名
        key_mapping = {
            'shadow_ratio': 'upper_shadow_ratio',  # gaps中是shadow_ratio，current_values中是upper_shadow_ratio
            'volume_min': 'volume_ratio',          # gaps中是volume_min，current_values中是volume_ratio
            'position_min': 'price_position',       # gaps中是position_min，current_values中是price_position
        }
        
        for item in results:
            result = item.get('result')
            if not result or 'gaps' not in result:
                continue
            
            gaps = result['gaps']
            current_values = result['current_values']
            
            # 统计各指标
            for key in condition_stats.keys():
                if key in gaps:
                    if gaps[key] >= 0:
                        condition_stats[key]['pass'] += 1
                    else:
                        condition_stats[key]['fail'] += 1
                
                # 收集实际值（使用映射后的键名）
                mapped_key = key_mapping.get(key, key)
                if mapped_key in current_values:
                    condition_stats[key]['values'].append(current_values[mapped_key])
        
        # 打印统计
        total = len(results)
        print(f"\n  分析时间点总数: {total}")
        
        # 列出所有分析的日期
        analyzed_dates = [item['date'] for item in results if item.get('result') is not None]
        failed_dates = [item['date'] for item in results if item.get('result') is None]
        
        if analyzed_dates:
            print(f"\n  ✅ 成功分析的日期: {', '.join(analyzed_dates)}")
        if failed_dates:
            print(f"  ❌ 分析失败的日期: {', '.join(failed_dates)}")
        
        print(f"\n  各指标达标情况:")
        
        has_any_data = any(stats['values'] for stats in condition_stats.values())
        if not has_any_data:
            print("    ⚠️  没有可用的统计数据（可能所有日期都分析失败）")
        
        for key, stats in condition_stats.items():
            values = stats['values']
            if not values:
                continue
            
            cn_name = indicator_names.get(key, key)
            pass_count = stats['pass']
            fail_count = stats['fail']
            pass_rate = (pass_count / total * 100) if total > 0 else 0
            
            min_val = min(values)
            max_val = max(values)
            avg_val = sum(values) / len(values) if values else 0
            
            print(f"\n    {cn_name} ({key}):")
            print(f"      达标率: {pass_rate:.1f}% ({pass_count}/{total})")
            # 根据指标类型选择显示格式
            if key in ['shadow_ratio', 'position_min']:
                # 比例和位置显示为百分比
                print(f"      范围: {min_val:.2%} ~ {max_val:.2%}")
                print(f"      平均: {avg_val:.2%}")
            else:
                # 量比等其他指标显示为小数
                print(f"      范围: {min_val:.2f} ~ {max_val:.2f}")
                print(f"      平均: {avg_val:.2f}")
        
        # 综合建议
        print(f"\n  💡 综合建议:")
        
        has_any_suggestion = False
        for key, stats in condition_stats.items():
            values = stats['values']
            if not values:
                continue
            has_any_suggestion = True
            
            cn_name = indicator_names.get(key, key)
            pass_rate = (stats['pass'] / total * 100) if total > 0 else 0
            
            min_val = min(values)
            max_val = max(values)
            
            if pass_rate < 50:  # 达标率低于50%
                # 建议降低到最小值的95%（确保能覆盖最小值）
                if key in ['shadow_ratio', 'position_min']:
                    suggested = min_val * 0.95
                    print(f"    - {cn_name}({key}): 达标率过低({pass_rate:.0f}%)，建议降至 {suggested:.2%} (最小值{min_val:.2%}×95%)")
                else:
                    suggested = min_val * 0.9
                    print(f"    - {cn_name}({key}): 达标率过低({pass_rate:.0f}%)，建议降至 {suggested:.2f} (最小值{min_val:.2f}×90%)")
            elif pass_rate > 90:  # 达标率高于90%
                print(f"    - {cn_name}({key}): 达标率很高({pass_rate:.0f}%)，当前阈值合理")
            else:  # 达标率50-90%
                # 即使达标率中等，也给出建议（基于最小值，确保所有案例都能触发）
                if key in ['shadow_ratio', 'position_min']:
                    suggested = min_val * 0.95
                    print(f"    - {cn_name}({key}): 达标率中等({pass_rate:.0f}%)，为覆盖所有案例，建议降至 {suggested:.2%} (最小值{min_val:.2%}×95%)")
                else:
                    suggested = min_val * 0.9
                    print(f"    - {cn_name}({key}): 达标率中等({pass_rate:.0f}%)，为覆盖所有案例，建议降至 {suggested:.2f} (最小值{min_val:.2f}×90%)")
        
        if not has_any_suggestion:
            print("    ⚠️  没有可用的建议（可能所有日期都分析失败）")
        
        # 测试调整后的参数
        self._test_adjusted_parameters(results, target_strategy, stock)
    
    def _test_adjusted_parameters(self, results, target_strategy, stock):
        """测试调整后的参数是否会触发信号"""
        print(f"\n{'='*70}")
        print("   🧪 参数调整测试")
        print("="*70)
        
        # 提取建议的参数值
        suggested_params = {}
        condition_stats = {
            'shadow_ratio': {'values': []},
            'position_min': {'values': []},
        }
        
        key_mapping = {
            'shadow_ratio': 'upper_shadow_ratio',
            'position_min': 'price_position',
        }
        
        for item in results:
            result = item.get('result')
            if not result or 'current_values' not in result:
                continue
            
            current_values = result['current_values']
            for key in condition_stats.keys():
                mapped_key = key_mapping.get(key, key)
                if mapped_key in current_values:
                    condition_stats[key]['values'].append(current_values[mapped_key])
        
        # 计算建议的参数值（基于最小值×95%）
        if condition_stats['shadow_ratio']['values']:
            min_shadow = min(condition_stats['shadow_ratio']['values'])
            suggested_params['shadow_ratio'] = min_shadow * 0.95
            print(f"\n✅ 建议调整：上影线比例 (shadow_ratio) = {suggested_params['shadow_ratio']:.2%}")
        
        if condition_stats['position_min']['values']:
            min_position = min(condition_stats['position_min']['values'])
            suggested_params['position_min'] = min_position * 0.95
            print(f"✅ 建议调整：价格位置 (position_min) = {suggested_params['position_min']:.2%}")
        
        if not suggested_params:
            print("\n⚠️  无法提取建议参数（可能数据不足）")
            return
        
        # 测试每个日期是否会用新参数触发
        print(f"\n{'─'*70}")
        print("测试结果：")
        print(f"{'─'*70}")
        
        from technical_engine.technical_filters import TechnicalEngine
        
        # 创建临时配置用于测试
        test_config = self.tech_config.copy()
        if 'upper_shadow' in test_config:
            # 获取当前股票类型（假设为大盘股）
            stock_type = self._get_stock_type(stock)
            cap_mapping = {'large': 'large_cap', 'medium': 'medium_cap', 'small': 'small_cap'}
            config_key = cap_mapping.get(stock_type, 'large_cap')
            
            # 更新配置为建议值
            if 'shadow_ratio' in suggested_params:
                test_config['upper_shadow']['sell_signals'][config_key]['shadow_ratio'] = suggested_params['shadow_ratio']
            if 'position_min' in suggested_params:
                test_config['upper_shadow']['sell_signals'][config_key]['position_min'] = suggested_params['position_min']
        
        # 初始化技术引擎（使用测试配置）
        tech_engine = TechnicalEngine(config=test_config)
        
        # 测试每个日期
        trigger_count = 0
        total_count = 0
        
        for item in results:
            result = item.get('result')
            if not result:
                continue
            
            date = item['date']
            current_values = result.get('current_values', {})
            
            if not current_values:
                continue
            
            total_count += 1
            
            # 检查是否满足条件
            upper_ratio = current_values.get('upper_shadow_ratio', 0)
            volume_ratio = current_values.get('volume_ratio', 0)
            price_position = current_values.get('price_position', 0)
            
            # 获取阈值
            stock_type = self._get_stock_type(stock)
            cap_mapping = {'large': 'large_cap', 'medium': 'medium_cap', 'small': 'small_cap'}
            config_key = cap_mapping.get(stock_type, 'large_cap')
            test_config_local = test_config['upper_shadow']['sell_signals'][config_key]
            
            shadow_ok = upper_ratio >= test_config_local.get('shadow_ratio', 0.35)
            volume_ok = volume_ratio >= test_config_local.get('volume_min', 0.8)
            position_ok = price_position >= test_config_local.get('position_min', 0.7)
            
            # ⭐ 重要：计算强度评分（实际回测中需要检查强度是否≥阈值）
            strength = self._calculate_strength_for_test(
                upper_ratio, volume_ratio, price_position, 
                test_config_local, test_config.get('upper_shadow', {})
            )
            min_strength = test_config.get('upper_shadow', {}).get('independent_mode', {}).get('min_strength', 2.5)
            strength_ok = strength >= min_strength
            
            # 基础条件都满足 AND 强度达标，才算真正触发
            all_ok = shadow_ok and volume_ok and position_ok and strength_ok
            
            if all_ok:
                trigger_count += 1
                status = "✅ 触发"
            else:
                status = "❌ 不触发"
                reasons = []
                if not shadow_ok:
                    reasons.append(f"上影线{upper_ratio:.2%}<{test_config_local.get('shadow_ratio', 0):.2%}")
                if not volume_ok:
                    reasons.append(f"量比{volume_ratio:.2f}<{test_config_local.get('volume_min', 0):.2f}")
                if not position_ok:
                    reasons.append(f"位置{price_position:.2%}<{test_config_local.get('position_min', 0):.2%}")
                if not strength_ok:
                    reasons.append(f"强度{strength:.2f}<{min_strength:.2f}")
                status += f" ({', '.join(reasons)})"
            
            print(f"  {date}: {status}")
            print(f"    上影线: {upper_ratio:.2%} (阈值: {test_config_local.get('shadow_ratio', 0):.2%}) | "
                  f"量比: {volume_ratio:.2f} (阈值: {test_config_local.get('volume_min', 0):.2f}) | "
                  f"位置: {price_position:.2%} (阈值: {test_config_local.get('position_min', 0):.2%}) | "
                  f"强度: {strength:.2f} (阈值: {min_strength:.2f})")
        
        print(f"\n{'─'*70}")
        print(f"✅ 测试完成：{trigger_count}/{total_count} 个日期会触发信号")
        if trigger_count == total_count:
            print("✅ 所有日期都能触发！参数调整建议合理。")
        elif trigger_count > 0:
            print(f"⚠️  部分日期能触发 ({trigger_count}/{total_count})，建议检查未触发的日期")
        else:
            print("❌ 没有日期能触发，参数可能设置过低")
    
    def _calculate_strength_for_test(self, upper_ratio, volume_ratio, price_position, sell_config, upper_shadow_config):
        """计算强度评分（用于测试）"""
        score = 0.0
        scoring = sell_config.get('scoring', {})
        
        # 1. 上影线因子（0-2分）
        shadow_scoring = scoring.get('shadow', {})
        if upper_ratio >= shadow_scoring.get('excellent', [0.35, 2.0])[0]:
            score += shadow_scoring['excellent'][1]
        elif upper_ratio >= shadow_scoring.get('good', [0.25, 1.5])[0]:
            score += shadow_scoring['good'][1]
        elif upper_ratio >= shadow_scoring.get('normal', [0.20, 1.0])[0]:
            score += shadow_scoring['normal'][1]
        
        # 2. 量能因子（0-1分）
        volume_scoring = scoring.get('volume', {})
        if volume_ratio >= volume_scoring.get('excellent', [2.0, 1.0])[0]:
            score += volume_scoring['excellent'][1]
        elif volume_ratio >= volume_scoring.get('good', [1.5, 0.5])[0]:
            score += volume_scoring['good'][1]
        
        # 3. 位置因子（0-1分）
        position_scoring = scoring.get('position', {})
        # ⭐ 从配置读取阈值（不是硬编码）
        excellent_threshold = position_scoring.get('excellent', [0.85, 1.0])[0]
        good_threshold = position_scoring.get('good', [0.75, 0.5])[0]
        if price_position >= excellent_threshold:
            score += position_scoring['excellent'][1]
        elif price_position >= good_threshold:
            score += position_scoring['good'][1]
        
        # 注意：测试中不计算盈利因子和趋势因子（需要实际持仓和历史数据）
        # 实际回测中会计算这些因子，强度可能会更高
        
        return min(score, 5.0)
    
    def _print_suggestions(self, target_strategy, metrics, suggestions, data_range=None):
        """打印分析结果"""
        # 指标中英文对照
        indicator_names = {
            'upper_shadow_ratio': '上影线比例',
            'volume_ratio': '量比',
            'price_position': '价格位置',
            'intraday_range': '日内振幅',
            'upper_shadow': '上影线长度',
            'high': '最高价',
            'current_price': '当前价',
            'low': '最低价',
            'shadow_ratio': '上影线阈值',
            'volume_min': '量比阈值',
            'position_min': '位置阈值',
            'min_kline_range': 'K线振幅阈值',
            'min_range': 'K线振幅',
        }
        
        # 打印数据时间范围（添加明显的日期标识）
        if data_range:
            analysis_date = data_range['analysis_date']
            print("\n" + "="*70)
            print(f"   📅 数据时间范围 - 【{analysis_date}】")
            print("="*70)
            print(f"  日线数据起始日期     : {data_range['start']}")
            print(f"  日线数据结束日期     : {data_range['end']} (前复权基准)")
            print(f"  分析日期             : {analysis_date}")
            print(f"  分析时间点           : {data_range['specific_time']}")
        
        # 在表格标题中也显示日期
        analysis_date = data_range.get('analysis_date', '') if data_range else ''
        date_label = f" - 【{analysis_date}】" if analysis_date else ""
        print("\n" + "="*110)
        print(f"   📊 指标分析对比表{date_label}")
        print("   ⭐ 说明: 参数建议基于日K线数据（全天数据），用于分时回测中触发类似形态")
        print("="*110)
        
        # 打印表头
        print(f"  {'指标名称':<12s} {'实际值':<15s} {'配置阈值':<15s} {'差异':<15s} {'结果':<8s} {'参数调整建议':<30s}")
        print("  " + "-"*106)
        
        # 关键指标列表（用于控制显示顺序和筛选）
        key_indicators = ['shadow_ratio', 'volume_min', 'position_min', 'min_kline_range']
        
        # 打印关键指标
        if 'current_values' in suggestions and 'current_thresholds' in suggestions and 'gaps' in suggestions:
            current_values = suggestions['current_values']
            current_thresholds = suggestions['current_thresholds']
            gaps = suggestions['gaps']
            
            for key in key_indicators:
                # ⭐ 获取值（主要使用全天数据/日K线数据）
                if key == 'shadow_ratio':
                    actual = current_values.get('upper_shadow_ratio', 0)  # 已改为使用全天数据
                elif key == 'volume_min':
                    actual = current_values.get('volume_ratio', 0)
                elif key == 'position_min':
                    actual = current_values.get('price_position', 0)
                elif key == 'min_kline_range':
                    actual = current_values.get('intraday_range', 0)  # 已改为使用全天振幅
                else:
                    continue
                
                threshold = current_thresholds.get(key, 0)
                gap = gaps.get(key, 0)
                
                # 格式化显示
                cn_name = indicator_names.get(key, key)
                
                if key == 'volume_min':
                    actual_str = f"{actual:.2f}倍"
                    threshold_str = f"{threshold:.2f}倍"
                    gap_str = f"{gap:+.2f}倍"
                elif 'ratio' in key or 'position' in key:
                    actual_str = f"{actual:.2%}"
                    threshold_str = f"{threshold:.2%}"
                    gap_str = f"{gap:+.2%}"
                else:
                    actual_str = f"{actual:.2f}元"
                    threshold_str = f"{threshold:.2f}元"
                    gap_str = f"{gap:+.2f}元"
                
                status = "✅ 满足" if gap >= 0 else "❌ 不足"
                
                # 生成调整建议
                if gap >= 0:
                    suggestion = "当前值已满足"
                else:
                    # 建议降至实际值的95%（留5%余量，确保类似形态能触发）
                    if key == 'volume_min':
                        suggested = actual * 0.9
                        suggestion = f"建议降至 {suggested:.2f}倍"
                    elif key in ['shadow_ratio', 'position_min']:
                        suggested = actual * 0.95
                        suggestion = f"建议降至 {suggested:.2%}"  # 显示为百分比格式
                    else:
                        suggested = actual * 0.9
                        suggestion = f"建议降至 {suggested:.2f}元"
                
                print(f"  {cn_name:<12s} {actual_str:<15s} {threshold_str:<15s} {gap_str:<15s} {status:<8s} {suggestion:<30s}")
        
        print("  " + "-"*106)
        
        # 辅助信息（不在表格中显示，单独列出）
        if 'current_values' in suggestions:
            current_values = suggestions['current_values']
            print("\n  📌 补充信息:")
            if 'daily_high' in current_values:
                daily_high = current_values.get('daily_high', 0)
                daily_close = current_values.get('daily_close', 0)
                daily_upper_shadow_ratio = current_values.get('daily_upper_shadow_ratio', 0)
                
                # ⭐ 主要显示日K线数据（用于参数建议）
                print(f"     【日K线数据】（⭐ 参数建议基于此数据）:")
                print(f"       开盘价: {current_values.get('open', 0):.2f}元  |  最高价: {daily_high:.2f}元  |  最低价: {current_values.get('daily_low', 0):.2f}元  |  收盘价: {daily_close:.2f}元")
                if daily_upper_shadow_ratio > 0:
                    daily_upper_shadow = current_values.get('daily_upper_shadow', 0)
                    daily_range = current_values.get('intraday_range', 0)  # 全天振幅
                    print(f"       上影线长度: {daily_upper_shadow:.2f}元  |  振幅: {daily_range:.2f}元  |  上影线比例: {daily_upper_shadow_ratio:.2%}")
                
                # 显示10:00数据对比（说明分时回测的特点）
                intraday_ratio = current_values.get('intraday_upper_shadow_ratio', 0)
                if intraday_ratio > 0:
                    current_price = current_values.get('current_price', 0)
                    print(f"     【10:00时数据】（分时回测判断时的状态）:")
                    print(f"       最高价: {current_values.get('high', 0):.2f}元  |  当前价: {current_price:.2f}元")
                    print(f"       上影线比例: {intraday_ratio:.2%}")
                    if abs(daily_upper_shadow_ratio - intraday_ratio) > 0.05:
                        print(f"       💡 提示: 10:00时上影线尚未完全形成，分时回测会在后续分钟中逐步判断")
        
        if 'message' in suggestions:
            print(f"\n  ℹ️  {suggestions['message']}")
        
        print("\n" + "="*110)


def main():
    """主函数"""
    parser = argparse.ArgumentParser(
        description='参数优化器 - 反推合理参数区间',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用示例：
  1. 直接运行（使用文件顶部的DEFAULT_CONFIG配置）：
     python parameter_optimizer.py
     
  2. 在DEFAULT_CONFIG中配置dates列表：
     'dates': ['20250210', '20250523', '20250527']
     
  3. 分析其他策略：
     python parameter_optimizer.py --target sell_A
        """
    )
    parser.add_argument('--stock', default=DEFAULT_CONFIG['stock'], help=f'股票代码 (默认: {DEFAULT_CONFIG["stock"]})')
    parser.add_argument('--target', default=DEFAULT_CONFIG['target'], help=f'目标策略 (默认: {DEFAULT_CONFIG["target"]})')
    parser.add_argument('--dividend-base', default=DEFAULT_CONFIG.get('dividend_base'), help=f'前复权基准日 (默认: {DEFAULT_CONFIG.get("dividend_base", "同分析日期")})')
    
    args = parser.parse_args()
    
    try:
        # 创建优化器
        optimizer = ParameterOptimizer()
        
        # 获取日期列表
        dates_list = DEFAULT_CONFIG.get('dates', [])
        
        if not dates_list:
            print(f"\n⚠️  错误: DEFAULT_CONFIG中的dates列表为空")
            print(f"请在 parameter_optimizer.py 文件顶部的 DEFAULT_CONFIG 中配置dates列表")
            print(f"示例：")
            print(f"  'dates': ['20250210', '20250523', '20250527']")
            return 1
        
        # 统一使用列表分析（支持单个或多个）
        count = len(dates_list)
        print(f"\n💡 提示: 分析 {count} 个时间点")
        print(f"💡 股票={args.stock}, 策略={args.target}\n")
        
        optimizer.analyze_multiple_dates(
            stock=args.stock,
            dates_list=dates_list,
            target_strategy=args.target,
            dividend_base=args.dividend_base
        )
        
        print("\n" + "="*70)
        print("   ✅ 分析完成！")
        print("="*70)
        
    except Exception as e:
        print(f"\n❌ 分析失败: {str(e)}")
        import traceback
        traceback.print_exc()
        return 1
    
    return 0


if __name__ == '__main__':
    main()

